iT邦幫忙

2022 iThome 鐵人賽

DAY 21
0
Modern Web

使用 Vue 3 從 0 到 1 架設網站!!!系列 第 21

Vue 頁面的登入狀態及登出

  • 分享至 

  • xImage
  •  

目前已經登入成功,那就先來將登入狀態處理一下,以及登出的功能。


驗證 JWT 的 API

在 typedefs.ts 檔案,先加上以下的 checkJWT 那行:

type Mutation {
  insertUser(nickname: String!, email: String!, password: String): User!
  updateUser(id: Int!, firstname: String!, lastname: String!): User!
  deleteUser(id: Int!): Boolean!
  authUser(username: String!, password: String!): String!
  checkJWT(jwt: String!): Boolean!
}

然後在 resolvers.ts,原來的:

export const resolvers = {
  Query: {
    hello: () => `Hello World!`,
    allUsers: () => allUsers()
  },
  Mutation: {
    insertUser: (_: any, args: any) => insertUser(args),
    updateUser: (_: any, args: any) => updateUser(args),
    deleteUser: (_: any, args: any) => deleteUser(args),
    authUser: (_: any, args: any) => authUser(args),
  }
};

改成(一樣僅加上 checkJWT 那行):

export const resolvers = {
  Query: {
    hello: () => `Hello World!`,
    allUsers: () => allUsers()
  },
  Mutation: {
    insertUser: (_: any, args: any) => insertUser(args),
    updateUser: (_: any, args: any) => updateUser(args),
    deleteUser: (_: any, args: any) => deleteUser(args),
    authUser: (_: any, args: any) => authUser(args),
    checkJWT: (_: any, args: any) => checkJWT(args)
  }
};

然後實作一下 checkJWT 函式,加以下的程式碼即可:

async function checkJWT(args){
  const payload = await verify(args.jwt, key);
  if(payload){
    return true;
  }
  return false;
}

經由以上的實作,其實就是會判斷由 Web APP 傳過來的 JWT 是否驗證通過,如果通過,就回傳 true,不通過的話就回傳 false。


Vue 頁面的登入狀態

登入的狀態,是不管在哪個頁面,重新整理頁面時,通常都會需要去判斷的,所以這部份,寫在 App.vue 檔案中。

修改 App.vue

在 App.vue 的 script 標籤當中,加以下的原始碼:
這段原始碼,其實就是將 jwt 傳送給後端,然後驗證該 jwt 是不是仍有效,如果通過,那麼 loginStatus 就設定 true;反之則為 false。

export default {
  data(){
    return {
      loginStatus: false
    };
  },
  created(){
    let jwt = localStorage.getItem("jwt");
    if(jwt){
      fetch("http://localhost:8080/graphql", {
        method: 'POST',
        body: JSON.stringify({
          query: `mutation {
            checkJWT(jwt: "${jwt}")
          }`
        })
      }).then(res => res.json()).then(body => {
        if(body.data.checkJWT){
          this.loginStatus = true;
        }else{
          this.loginStatus = false;
        }
      });
    }
  }
}

然後,在 template 標籤當中,以下這行:

component(:is="Component" :key="$route.path")

改成:

component(:is="Component" :key="$route.path" :login-status="loginStatus")

也就是透過 props 的概念,將 loginStatus 資料,往子元件去傳遞。

修改 Home.vue

先將 src/views/Home.vue 檔案當中,以下這行移除:

router-link(:to="{name: 'WebBuild'}" style="color: white;") 進到建立網頁的後台(使用 router-link)

然後在 src/views/Home.vue 檔案當中,加以下 props 設定,用來承接從父層元件傳遞過來的屬性:

export default {
    
    props: ["loginStatus"], // 加這行
    
    // 以下這邊是其它程式
}

而在 template 標籤當中呢,原來的表單,改成以下:
這段程式呢,留意 v-if 和 v-else 的部份,也就是用來判斷目前登入的狀態是什麼(已登入或未登入),然後顯示對應的介面。

div.login_block(v-if="loginStatus")
  router-link(:to="{name: 'WebBuild'}" class="web_build") 後台建立網頁
  button(type="button" @click="signout") 登出
form(v-else action="#" method="#" class="login_form")
  div.input_group
    label 帳號
    input(type="text" autocomplete="username" v-model="email")
  div.input_group
    label 密碼
    input(type="password" autocomplete="current-password" v-model="password")
  div.input_group
    label
    button(type="button" @click="auth_user") 登入
    router-link(:to="{name: 'register'}" class="register") 還不是會員嗎?請先註冊成為會員

然後在 style 標籤中,新增以下的 css:

div.login_block
  a.web_build
    color: white
    font-size: 1.8rem
    background-image: radial-gradient(100% 100% at 100% 0, rgba(0,172,193,1) 0, #4e5aba 100%)
    border: 0
    border-radius: 6px
    box-shadow: rgba(45, 35, 66, .4) 0 2px 4px,rgba(45, 35, 66, .3) 0 7px 13px -3px,rgba(58, 65, 111, .5) 0 -3px 0 inset
    color: #fff
    display: inline-block
    height: 48px
    line-height: 48px
    padding-left: 16px
    padding-right: 16px
    text-decoration: none
    &:focus
      box-shadow: #3c4fe0 0 0 0 1.5px inset, rgba(45, 35, 66, .4) 0 2px 4px, rgba(45, 35, 66, .3) 0 7px 13px -3px, #3c4fe0 0 -3px 0 inset
    &:hover
      box-shadow: rgba(45, 35, 66, .4) 0 4px 8px, rgba(45, 35, 66, .3) 0 7px 13px -3px, #3c4fe0 0 -3px 0 inset
      transform: translateY(-2px)
    &:active
      box-shadow: #3c4fe0 0 3px 7px inset
      transform: translateY(2px)

登入成功,首頁的畫面

將以上的程式修改完成後,就可以在首頁的登入部份,實際測試看看,登入成功的話,首頁的頁面會重新整理,就會看到如下圖:

https://ithelp.ithome.com.tw/upload/images/20220921/20069901vcxpNKFCer.png

右側有兩個按鈕,左邊那個按鈕,點下去就是會進到未來要做的頁面(之前有建立過);右邊的按鈕呢,就是登出按鈕。


登出功能

在 src/views/Home.vue 當中,在 methods 屬性當中,加一個函式叫做 signout 函式,如下,以下列出 methods 的部份:

methods: {
  auth_user(){
    // 這裡有其它程式碼
  },
  signout(){ // 加這個函式
    localStorage.removeItem("jwt");
    alert("登出成功");
    location.reload();
  }
}

所以登出功能很簡單,就是將 localStorage 中的 jwt 資料給移除即可。登出後,頁面會重新整理,就會出現如下圖(未登入的介面):

https://ithelp.ithome.com.tw/upload/images/20220921/20069901V7c6v98mdV.png


結語

目前首頁可以區分 已登入狀態未登入狀態 了,其實有用 Vue 當中的 provide/injdect 的部份,但有個地方怪怪的,所以還是先用 props 的方式將資料往內層元件做傳遞,也是ok的。


上一篇
登入的 API 串接,取得 JWT 後,存至 localStorage
下一篇
登出功能,練習寫一下使用 $emit 觸發自訂事件
系列文
使用 Vue 3 從 0 到 1 架設網站!!!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言